/*-------------------------------------------------------------------------
 *
 * hashfunc.c
 *	  哈希访问方法的支持函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/access/hash/hashfunc.c
 *
 * NOTES
 *	  这些函数存储在pg_amproc中。对于为哈希索引定义的每个操作类，它们计算参数的哈希值。
 *
 *	  额外的哈希函数出现在/utils/adt/文件中，适用于各种特殊数据类型。
 *
 *	  预计哈希函数32位结果的每一位都与其他位一样随机；未能确保这一点可能导致哈希连接等的性能不佳。
 *	  在大多数情况下，哈希函数应使用hash_any()或其变体hash_uint32()。
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/hash.h"
#include "catalog/pg_collation.h"
#include "common/hashfn.h"
#include "utils/builtins.h"
#include "utils/float.h"
#include "utils/pg_locale.h"

/*
 * 数据类型特定的哈希函数。
 *
 * 这些支持哈希索引和哈希连接。
 *
 * 注意：其中一些也被catcache操作使用，而与哈希索引没有直接联系。此外，通用的hash_any
 * 例程也被dynahash表使用。
 */

/* 注意：这适用于“char”和布尔数据类型 */
Datum hashchar(PG_FUNCTION_ARGS)
{
	return hash_uint32((int32) PG_GETARG_CHAR(0));
}

Datum hashcharextended(PG_FUNCTION_ARGS)
{
	return hash_uint32_extended((int32) PG_GETARG_CHAR(0), PG_GETARG_INT64(1));
}

Datum hashint2(PG_FUNCTION_ARGS)
{
	return hash_uint32((int32) PG_GETARG_INT16(0));
}

Datum hashint2extended(PG_FUNCTION_ARGS)
{
	return hash_uint32_extended((int32) PG_GETARG_INT16(0), PG_GETARG_INT64(1));
}

Datum hashint4(PG_FUNCTION_ARGS)
{
	return hash_uint32(PG_GETARG_INT32(0));
}

Datum hashint4extended(PG_FUNCTION_ARGS)
{
	return hash_uint32_extended(PG_GETARG_INT32(0), PG_GETARG_INT64(1));
}

Datum hashint8(PG_FUNCTION_ARGS)
{
	/*
	 * 这里的想法是产生一个与hashint4和hashint2为逻辑上相等的输入生成的值兼容的哈希值；
	 * 这对于支持这些输入类型之间的跨类型哈希连接是必要的。
	 * 由于这三种类型都是有符号的，当符号为正时，我们可以对int8
	 * 值的高半部分进行异或操作，或者当符号为负时对高半部分取反。
	 */
	int64		fc_val = PG_GETARG_INT64(0);
	uint32		fc_lohalf = (uint32) fc_val;
	uint32		fc_hihalf = (uint32) (fc_val >> 32);

	fc_lohalf ^= (fc_val >= 0) ? fc_hihalf : ~fc_hihalf;

	return hash_uint32(fc_lohalf);
}

Datum hashint8extended(PG_FUNCTION_ARGS)
{
	/* 与hashint8相同的方法 */
	int64		fc_val = PG_GETARG_INT64(0);
	uint32		fc_lohalf = (uint32) fc_val;
	uint32		fc_hihalf = (uint32) (fc_val >> 32);

	fc_lohalf ^= (fc_val >= 0) ? fc_hihalf : ~fc_hihalf;

	return hash_uint32_extended(fc_lohalf, PG_GETARG_INT64(1));
}

Datum hashoid(PG_FUNCTION_ARGS)
{
	return hash_uint32((uint32) PG_GETARG_OID(0));
}

Datum hashoidextended(PG_FUNCTION_ARGS)
{
	return hash_uint32_extended((uint32) PG_GETARG_OID(0), PG_GETARG_INT64(1));
}

Datum hashenum(PG_FUNCTION_ARGS)
{
	return hash_uint32((uint32) PG_GETARG_OID(0));
}

Datum hashenumextended(PG_FUNCTION_ARGS)
{
	return hash_uint32_extended((uint32) PG_GETARG_OID(0), PG_GETARG_INT64(1));
}

Datum hashfloat4(PG_FUNCTION_ARGS)
{
	float4		fc_key = PG_GETARG_FLOAT4(0);
	float8		fc_key8;

	/*
	 * 在IEEE浮动机器上，负零和零具有不同的位模式
	 * 但应视为相等。我们必须确保它们具有相同的
	 * 哈希值，这样做最可靠：
	 */
	if (fc_key == (float4) 0)
		PG_RETURN_UINT32(0);

	/*
	 * 为了支持float8和float4的跨类型哈希，我们希望返回
	 * hashfloat8为相等的float8值生成的相同哈希值。
	 * 因此，将值扩展为float8并对其进行哈希处理。（我们必须这样做，
	 * 而不是让hashfloat8尝试将其值缩小到float4；那样在溢出时可能会失败。）
	 */
	fc_key8 = fc_key;

	/*
	 * 同样，NaNs可能具有不同的位模式，但它们都应
	 * 被视为相等。出于向后兼容性原因，我们强制它们
	 * 具有标准float8 NaN的哈希值。（你可能会认为我们可以
	 * 用一个float4 NaN替换key然后扩展它；但在一些旧的平台上，
	 * 这种方法产生不同的位模式。）
	 */
	if (isnan(fc_key8))
		fc_key8 = get_float8_nan();

	return hash_any((unsigned char *) &fc_key8, sizeof(fc_key8));
}

Datum hashfloat4extended(PG_FUNCTION_ARGS)
{
	float4		fc_key = PG_GETARG_FLOAT4(0);
	uint64		fc_seed = PG_GETARG_INT64(1);
	float8		fc_key8;

	/* 与hashfloat4相同的方法 */
	if (fc_key == (float4) 0)
		PG_RETURN_UINT64(fc_seed);
	fc_key8 = fc_key;
	if (isnan(fc_key8))
		fc_key8 = get_float8_nan();

	return hash_any_extended((unsigned char *) &fc_key8, sizeof(fc_key8), fc_seed);
}

Datum hashfloat8(PG_FUNCTION_ARGS)
{
	float8		fc_key = PG_GETARG_FLOAT8(0);

	/*
	 * 在IEEE浮动机器上，负零和零具有不同的位模式
	 * 但应视为相等。我们必须确保它们具有相同的
	 * 哈希值，这样做最可靠：
	 */
	if (fc_key == (float8) 0)
		PG_RETURN_UINT32(0);

	/*
	 * 同样，NaNs可能具有不同的位模式，但它们都应
	 * 被视为相等。出于向后兼容性原因，我们强制它们
	 * 具有标准NaN的哈希值。
	 */
	if (isnan(fc_key))
		fc_key = get_float8_nan();

	return hash_any((unsigned char *) &fc_key, sizeof(fc_key));
}

Datum hashfloat8extended(PG_FUNCTION_ARGS)
{
	float8		fc_key = PG_GETARG_FLOAT8(0);
	uint64		fc_seed = PG_GETARG_INT64(1);

	/* 与hashfloat8相同的方法 */
	if (fc_key == (float8) 0)
		PG_RETURN_UINT64(fc_seed);
	if (isnan(fc_key))
		fc_key = get_float8_nan();

	return hash_any_extended((unsigned char *) &fc_key, sizeof(fc_key), fc_seed);
}

Datum hashoidvector(PG_FUNCTION_ARGS)
{
	oidvector  *fc_key = (oidvector *) PG_GETARG_POINTER(0);

	return hash_any((unsigned char *) fc_key->values, fc_key->dim1 * sizeof(Oid));
}

Datum hashoidvectorextended(PG_FUNCTION_ARGS)
{
	oidvector  *fc_key = (oidvector *) PG_GETARG_POINTER(0);

	return hash_any_extended((unsigned char *) fc_key->values,
							 fc_key->dim1 * sizeof(Oid),
							 PG_GETARG_INT64(1));
}

Datum hashname(PG_FUNCTION_ARGS)
{
	char	   *fc_key = NameStr(*PG_GETARG_NAME(0));

	return hash_any((unsigned char *) fc_key, strlen(fc_key));
}

Datum hashnameextended(PG_FUNCTION_ARGS)
{
	char	   *fc_key = NameStr(*PG_GETARG_NAME(0));

	return hash_any_extended((unsigned char *) fc_key, strlen(fc_key),
							 PG_GETARG_INT64(1));
}

Datum hashtext(PG_FUNCTION_ARGS)
{
	text	   *fc_key = PG_GETARG_TEXT_PP(0);
	Oid			fc_collid = PG_GET_COLLATION();
	pg_locale_t fc_mylocale = 0;
	Datum		fc_result;

	if (!fc_collid)
		ereport(ERROR,
				(errcode(ERRCODE_INDETERMINATE_COLLATION),
				 errmsg("could not determine which collation to use for string hashing"),
				 errhint("Use the COLLATE clause to set the collation explicitly.")));

	if (!lc_collate_is_c(fc_collid))
		fc_mylocale = pg_newlocale_from_collation(fc_collid);

	if (!fc_mylocale || fc_mylocale->deterministic)
	{
		fc_result = hash_any((unsigned char *) VARDATA_ANY(fc_key),
						  VARSIZE_ANY_EXHDR(fc_key));
	}
	else
	{
#ifdef USE_ICU
		if (fc_mylocale->provider == COLLPROVIDER_ICU)
		{
			int32_t		fc_ulen = -1;
			UChar	   *fc_uchar = NULL;
			Size		fc_bsize;
			uint8_t    *fc_buf;

			fc_ulen = icu_to_uchar(&fc_uchar, VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));

			fc_bsize = ucol_getSortKey(fc_mylocale->info.icu.ucol,
									fc_uchar, fc_ulen, NULL, 0);
			fc_buf = palloc(fc_bsize);
			ucol_getSortKey(fc_mylocale->info.icu.ucol,
							fc_uchar, fc_ulen, fc_buf, fc_bsize);
			pfree(fc_uchar);

			fc_result = hash_any(fc_buf, fc_bsize);

			pfree(fc_buf);
		}
		else
#endif
			/* 不应该发生 */
			elog(ERROR, "unsupported collprovider: %c", fc_mylocale->provider);
	}

	/* 避免对烤制输入泄露内存 */
	PG_FREE_IF_COPY(fc_key, 0);

	return fc_result;
}

Datum hashtextextended(PG_FUNCTION_ARGS)
{
	text	   *fc_key = PG_GETARG_TEXT_PP(0);
	Oid			fc_collid = PG_GET_COLLATION();
	pg_locale_t fc_mylocale = 0;
	Datum		fc_result;

	if (!fc_collid)
		ereport(ERROR,
				(errcode(ERRCODE_INDETERMINATE_COLLATION),
				 errmsg("could not determine which collation to use for string hashing"),
				 errhint("Use the COLLATE clause to set the collation explicitly.")));

	if (!lc_collate_is_c(fc_collid))
		fc_mylocale = pg_newlocale_from_collation(fc_collid);

	if (!fc_mylocale || fc_mylocale->deterministic)
	{
		fc_result = hash_any_extended((unsigned char *) VARDATA_ANY(fc_key),
								   VARSIZE_ANY_EXHDR(fc_key),
								   PG_GETARG_INT64(1));
	}
	else
	{
#ifdef USE_ICU
		if (fc_mylocale->provider == COLLPROVIDER_ICU)
		{
			int32_t		fc_ulen = -1;
			UChar	   *fc_uchar = NULL;
			Size		fc_bsize;
			uint8_t    *fc_buf;

			fc_ulen = icu_to_uchar(&fc_uchar, VARDATA_ANY(fc_key), VARSIZE_ANY_EXHDR(fc_key));

			fc_bsize = ucol_getSortKey(fc_mylocale->info.icu.ucol,
									fc_uchar, fc_ulen, NULL, 0);
			fc_buf = palloc(fc_bsize);
			ucol_getSortKey(fc_mylocale->info.icu.ucol,
							fc_uchar, fc_ulen, fc_buf, fc_bsize);
			pfree(fc_uchar);

			fc_result = hash_any_extended(fc_buf, fc_bsize, PG_GETARG_INT64(1));

			pfree(fc_buf);
		}
		else
#endif
			/* 不应该发生 */
			elog(ERROR, "unsupported collprovider: %c", fc_mylocale->provider);
	}

	PG_FREE_IF_COPY(fc_key, 0);

	return fc_result;
}

/*
 * hashvarlena() 可用于任何没有非显著位的varlena数据类型，即，
 * 不同的位模式从不视为相等。
 */
Datum hashvarlena(PG_FUNCTION_ARGS)
{
	struct varlena *fc_key = PG_GETARG_VARLENA_PP(0);
	Datum		fc_result;

	fc_result = hash_any((unsigned char *) VARDATA_ANY(fc_key),
					  VARSIZE_ANY_EXHDR(fc_key));

	/* 避免对烤制输入泄露内存 */
	PG_FREE_IF_COPY(fc_key, 0);

	return fc_result;
}

Datum hashvarlenaextended(PG_FUNCTION_ARGS)
{
	struct varlena *fc_key = PG_GETARG_VARLENA_PP(0);
	Datum		fc_result;

	fc_result = hash_any_extended((unsigned char *) VARDATA_ANY(fc_key),
							   VARSIZE_ANY_EXHDR(fc_key),
							   PG_GETARG_INT64(1));

	PG_FREE_IF_COPY(fc_key, 0);

	return fc_result;
}
